package mywal

import (
	"encoding/binary"
	"fmt"
	"hash/crc32"
	"io"
	"os"
	"sync"
)

type RecordType int

const (
	RecordTypeZero RecordType = iota
	RecordTypeFull
	RecordTypeFirst
	RecordTypeMiddle
	RecordTypeLast
)

type SharedLock struct {
	locker sync.RWMutex
}

type WAL struct {
	file *os.File
	mu   *SharedLock
}

const (
	recordHeaderSize = 7         // 1字节类型 + 2字节长度 + 4字节CRC
	maxPayloadSize   = 32 * 1024 // 32KB
	recordSize       = recordHeaderSize + maxPayloadSize
)

func NewWAL(filename string) (*WAL, error) {
	file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
	if err != nil {
		return nil, err
	}

	return &WAL{file: file, mu: &SharedLock{}}, nil
}

func (wal *WAL) Write(data []byte) error {
	wal.mu.locker.Lock()
	defer wal.mu.locker.Unlock()

	dataLen := len(data)
	offset := 0

	record := make([]byte, recordSize)
	for offset < dataLen {
		remainingBytes := dataLen - offset
		payloadSize := min(maxPayloadSize, remainingBytes)

		// 确定记录类型
		var rt RecordType
		if offset == 0 {
			if payloadSize == dataLen {
				rt = RecordTypeFull
			} else {
				rt = RecordTypeFirst
			}
		} else if offset+payloadSize == dataLen {
			rt = RecordTypeLast
		} else {
			rt = RecordTypeMiddle
		}

		// 准备记录
		record[0] = byte(rt)
		binary.LittleEndian.PutUint16(record[1:3], uint16(payloadSize))

		// 复制数据
		copy(record[recordHeaderSize:], data[offset:offset+payloadSize])

		// 计算并写入CRC
		crc := crc32.ChecksumIEEE(record[recordHeaderSize : recordHeaderSize+payloadSize])
		binary.LittleEndian.PutUint32(record[3:7], crc)

		// 写入记录
		_, err := wal.file.Write(record)
		if err != nil {
			return err
		}

		offset += payloadSize
	}

	// 确保数据写入磁盘
	return wal.file.Sync()
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// Record 结构体定义
type Record struct {
	Type   RecordType
	Length uint16
	CRC    uint32
	Data   []byte
}

type Iterator struct {
	file   os.File
	offset int64
	mu     *SharedLock
}

func (wal *WAL) NewIterator() (*Iterator, error) {
	wal.mu.locker.Lock()
	defer wal.mu.locker.Unlock()

	_, err := wal.file.Seek(0, io.SeekStart)
	if err != nil {
		return nil, err
	}
	return &Iterator{file: *wal.file, offset: 0, mu: wal.mu}, nil
}

func (it *Iterator) Next() ([]byte, error) {
	it.mu.locker.RLock()
	defer it.mu.locker.RUnlock()

	var fullData []byte

	record := make([]byte, recordSize)
	for {
		_, err := io.ReadFull(&it.file, record)
		if err != nil {
			if err == io.EOF {
				return nil, io.EOF
			}
			return nil, err
		}

		// 解析记录头
		recordType := RecordType(record[0])
		payloadSize := binary.LittleEndian.Uint16(record[1:3])
		crc := binary.LittleEndian.Uint32(record[3:7])

		// 验证CRC
		if crc32.ChecksumIEEE(record[recordHeaderSize:recordHeaderSize+payloadSize]) != crc {
			return nil, fmt.Errorf("CRC校验失败")
		}

		// 提取有效载荷
		payload := record[recordHeaderSize : recordHeaderSize+payloadSize]

		// 处理不同的记录类型
		switch recordType {
		case RecordTypeFull:
			return payload, nil
		case RecordTypeFirst:
			fullData = payload
		case RecordTypeMiddle:
			fullData = append(fullData, payload...)
		case RecordTypeLast:
			fullData = append(fullData, payload...)
			return fullData, nil
		default:
			return nil, fmt.Errorf("未知记录类型: %d", recordType)
		}

		it.offset += recordSize
	}
}

func (wal *WAL) Iter() (*Iterator, error) {
	return wal.NewIterator()
}

func (wal *WAL) Close() error {
	wal.mu.locker.Lock()
	defer wal.mu.locker.Unlock()

	return wal.file.Close()
}
